package com.haiyou.data.common.accessor.caffeine;


import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.springframework.util.ObjectUtils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import com.google.common.collect.Sets;
import com.haiyou.common.entity.Entity;
import com.haiyou.common.entity.PlayerEntity;
import com.haiyou.common.system.Properties;
import com.haiyou.common.util.string.StringFormatUitls;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 
 * @Description: caffine k v 缓存
 * @author xingyuan
 * @date 2022年12月2日 下午10:00:10
 */
@Slf4j
public class CaffeineCache<V extends Entity<?>> {

	/**
	 * 缓存
	 */
	private Cache<String, V> cache;
	
	private Cache<Long,Set<String>> keyCache;
	
	public final static String mapper_key_format = "{}-mapper-{}";
	
	private static final class Lazy {
		private static final CaffeineCache<Entity<?>> instance;
		static {
			instance = new CaffeineCache<Entity<?>>();
		}
	}
	
	private CaffeineCache() {
		//初始化
		init();
	}
	
	public static CaffeineCache<Entity<?>> getInstance() {
		return Lazy.instance;
	}
	
	private void init() {
		//缓存
		cache = Caffeine.newBuilder()
				//设置一个访问过期时间
				.expireAfterAccess(Properties.caffine_expire_time(), TimeUnit.SECONDS)
				//设置缓存大小
				.maximumSize(Properties.caffine_maximum_size())
				.build();
		
		keyCache = Caffeine.newBuilder()
				//设置一个访问过期时间
				.expireAfterAccess(Properties.caffine_expire_time() * 2, TimeUnit.SECONDS)
				//设置缓存大小
				.maximumSize(Properties.caffine_maximum_size() * 2)
				
				.build();
		
		log.info("init caffine expire {},maximumSize {}",Properties.caffine_expire_time(),Properties.caffine_maximum_size());
	}



	
	/**
	 * 基于玩家id 过期 keys
	 * @param playerId
	 */
	public void restKeyCache(long playerId) {
		
		Set<String> keys = keyCache.getIfPresent(playerId);
		
		if(ObjectUtils.isEmpty(keys)) {
			return ;
		}
		
		Set<String> key1s = Sets.newHashSet(keys);
		
		//移除key
		cache.invalidateAll(key1s);
		//移除主键key
		keyCache.invalidate(playerId);
	}

	/**
	 * 缓存玩家 key
	 * @param <A>
	 * @param values
	 * @return
	 */
	public <A extends Entity<?>> Map<String, A> cacheKey(Map<String, A> values){
		
		if(ObjectUtils.isEmpty(values)) {
			return values;
		}
		
		Set<String> keys = null;
		
		for (String key : values.keySet()) {
			
			A value = values.get(key);
			
			if(!(value instanceof PlayerEntity)) {
				return values;
			}

			PlayerEntity<?> v = (PlayerEntity<?>)value;

			if(keys == null) {
				keys = keyCache.get(v.getPlayerId(), k->Sets.newHashSet());
			}
			
			keys.add((String)value.getId());
			keyCache.put(v.getPlayerId(), keys);
		}
	
		return values;
	}
	
	/**
	 * 缓存玩家 key
	 * @param <A>
	 * @param value
	 * @return
	 */
	public <A extends Entity<?>> A cacheKey(A value) {
		
		if(value == null) {
			return value;
		}
		
		if(!(value instanceof PlayerEntity)) {
			return value;
		}
		
		PlayerEntity<?> v = (PlayerEntity<?>)value;
		
		Set<String> keys = keyCache.get(v.getPlayerId(), k->Sets.newHashSet());
		
		keys.add((String)value.getId());
		keyCache.put(v.getPlayerId(), keys);
		
		return value;
	}
	
	
	@SuppressWarnings("unchecked")
	public <A extends Entity<?>> A get(@NonNull String key,@NonNull Function<String, V> mappingFunction){
		return cacheKey((A)cache.get(key, mappingFunction));
	}
	
	@SuppressWarnings("unchecked")
	public <A extends Entity<?>> A get(@NonNull String key){
		return cacheKey((A)cache.getIfPresent(key));
	}

	@SuppressWarnings("unchecked")
	public <A extends Entity<?>> Map<String, A> getAll(Iterable<? extends String> keys){
		return cacheKey((Map<String, A>) cache.getAllPresent(keys));
	}
	
	@SuppressWarnings("unchecked")
	public <A extends Entity<?>> A put(String key,A value) {
		cache.put(key, (V)value);
		return cacheKey(value);
	}
	
	public <A extends Entity<?>> A remove(String key) {
		A value = get(key);
		if(value == null) {
			return value;
		}
		cache.invalidate(key);
		return value;
	}

	@SuppressWarnings("unchecked")
	public <A extends Entity<?>> void updateCacheKey(String key,A value) {
		cache.put(key, (V)value);
	}
	
	
	public void removeAll(Iterable<? extends String> keys) {
		if(ObjectUtils.isEmpty(keys)) {
			return ;
		}
		
		Set<String> key1s = Sets.newHashSet(keys);
		
		cache.invalidateAll(key1s);
	}

	
	/**
	 * 获取 Bucket 对象
	 * @param <A>
	 * @param key
	 * @param mappingFunction
	 * @return
	 */
	public <A extends Entity<?>> A getBucket(@NonNull String key,@NonNull Function<String, V> mappingFunction) {
		return get(key,mappingFunction);
	}
	
	public <A extends Entity<?>> A getBucket(@NonNull String key) {
		return get(key);
	}
	
	public <A extends Entity<?>> A putBucket(String key,A value) {
		return put(key, value);
	}
	
	public <A extends Entity<?>> Map<String, A> getAllBucket(Iterable<? extends String> keys){
		return getAll(keys);
	}
	
	public <A extends Entity<?>> A removeBucket(String key) {
		return remove(key);
	}
	
	public void removeAllBucket(Iterable<? extends String> keys) {
		Set<String> key1s = Sets.newHashSet(keys);
		removeAll(key1s);
	}

	
	/**
	 * 获取 mapper 内对象
	 * @param <A>
	 * @param key
	 * @param mappingFunction
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <A extends Entity<?>,Id extends Serializable & Comparable<Id>> A getMapper(@NonNull String prefix,@NonNull Id id,@NonNull Function<String, V> mappingFunction) {
		return (A)cache.get(mapperKey(prefix, id), mappingFunction);
	}
	
	/**
	 * 转换key
	 * @param <Id>
	 * @param prefix
	 * @param id
	 * @return
	 */
	public <Id extends Serializable & Comparable<Id>> String mapperKey(String prefix,Id id) {
		return StringFormatUitls.format(mapper_key_format, prefix,id);
	}
	
}